package cc.shacocloud.mirage.bean;

import cc.shacocloud.mirage.bean.exception.BeanException;
import cc.shacocloud.mirage.utils.annotation.AnnotatedElementUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.annotation.Annotation;
import java.util.Map;

/**
 * 对象工厂
 *
 * @author 思追(shaco)
 */
public interface BeanFactory {
    
    /**
     * 根据对象名称获取对象
     *
     * @param name 对象名称
     * @return T 返回的对象实例
     * @throws BeanException 如果无法获取对象则抛出该例外
     */
    @NotNull <T> T getBean(@NotNull String name) throws BeanException;
    
    /**
     * 根据对象名称和对象类型获取对象
     * <p>
     * 注： 对象名称是唯一的，对象类型只是用来判断是否可以转换，如果不可以将抛出例外 {@link ClassCastException}
     *
     * @param name    对象名称
     * @param classes 对象类型
     * @return T 返回的对象实例
     * @throws BeanException 如果无法获取对象则抛出该例外
     */
    @NotNull
    @SuppressWarnings("unchecked")
    default <T> T getBean(@NotNull String name, @NotNull Class<T> classes) throws BeanException, ClassCastException {
        Object bean = getBean(name);
        
        if (classes.isAssignableFrom(bean.getClass())) {
            return (T) bean;
        }
        
        throw new ClassCastException();
    }
    
    /**
     * 根据对象类型获取对象
     * <p>
     * 如果该类型指定存在多个实现，并且未指定优先使用的对象，将抛出异常
     *
     * @param classes 对象类型
     * @return T 返回的对象实例
     * @throws BeanException 如果无法获取对象则抛出该例外
     */
    @NotNull <T> T getBean(@NotNull Class<T> classes) throws BeanException;
    
    /**
     * 判断指定的对象名称是否存在与当前对象工厂中
     *
     * @param name 对象名称
     * @return 存在则返回 true 反之为 false
     */
    boolean containsBean(@NotNull String name);
    
    /**
     * 判断指定的对象类型是否存在与当前对象工厂中
     * <p>
     * 包含该对象的子类型
     *
     * @param classes 对象类型
     * @return 存在则返回 true 反之为 false
     */
    <T> boolean containsBean(@NotNull Class<T> classes);
    
    /**
     * 根据指定类型获取当前对象工厂中所有的实现
     * <p>
     * 包含该对象的子类型
     *
     * @param classes 对象类型
     * @return 返回Map，key 为对象的名称，value 为对象的实例
     */
    @NotNull <T> Map<String, T> getBeanByType(@NotNull Class<T> classes);
    
    /**
     * 根据对象名称获取对象类型
     *
     * @param name 对象名称
     * @return 对象类型
     * @throws BeanException 如果无法获取对象则抛出该例外
     */
    @NotNull <T> Class<T> getBeanType(String name) throws BeanException;
    
    /**
     * 获取 bean 对象上的注解
     *
     * @param beanName       对象名称
     * @param annotationType 注解名称
     * @return 对象上的注解
     * @throws BeanException 如果无法获取对象则抛出该例外
     */
    @Nullable
    default <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) throws BeanException {
        Class<?> beanType = getBeanType(beanName);
        return AnnotatedElementUtils.getAnnotation(beanType, annotationType);
    }
    
    /**
     * 根据对象类型获取对应的对象名称
     * <p>
     * 包含该对象的子类型
     *
     * @param classes 对象类型
     * @return 对象名称
     */
    <T> String[] getBeanNamesForType(Class<T> classes);
    
    /**
     * 根据指定类型获取当前对象工厂中所有可以注入的类型
     * <p>
     * 包含该对象的子类型
     *
     * @param classes 对象类型
     * @return 返回Map，key 为对象的名称，value 为对象的实例
     */
    @NotNull <T> Map<String, Class<? extends T>> getBeanClassForType(@NotNull Class<T> classes);
    
    /**
     * 根据注解获取对应的对象名称
     *
     * @param annotationType 注解类型
     * @param <A>            注解类型
     * @return 返回声明该注解的对象名称
     */
    <A extends Annotation> String[] getBeanNamesForAnnotation(Class<A> annotationType);
    
}
